/*
 * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"

#include "esp_http_server.h"
#include "img_converters.h"
#include "sdkconfig.h"
#include "esp_log.h"
#include "driver/gpio.h"

#define LED_NUM_33 (33)
#define LED_NUM_4 (4)


#define LED_NUM_12 (12)
#define LED_NUM_13 (13)


#define PART_BOUNDARY "123456789000000000000987654321"
static const char *_STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY;
static const char *_STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n";
static const char *_STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\nX-Timestamp: %d.%06d\r\n\r\n";
static QueueHandle_t xQueueFrameI = NULL;
static bool gReturnFB = true;


httpd_handle_t camera_httpd = NULL;
httpd_handle_t stream_httpd = NULL;



static const char *TAG = "stream";


static esp_err_t stream_handler(httpd_req_t *req)
{
    camera_fb_t *frame = NULL;
    struct timeval _timestamp;
    esp_err_t res = ESP_OK;
    size_t _jpg_buf_len = 0;
    uint8_t *_jpg_buf = NULL;
    char *part_buf[128];

    res = httpd_resp_set_type(req, _STREAM_CONTENT_TYPE);
    if (res != ESP_OK) {
        return res;
    }

    httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
    httpd_resp_set_hdr(req, "X-Framerate", "60");

    while (true) {
        if (xQueueReceive(xQueueFrameI, &frame, portMAX_DELAY)) {
            _timestamp.tv_sec = frame->timestamp.tv_sec;
            _timestamp.tv_usec = frame->timestamp.tv_usec;

            if (frame->format == PIXFORMAT_JPEG) {
                _jpg_buf = frame->buf;
                _jpg_buf_len = frame->len;
            } else if (!frame2jpg(frame, 60, &_jpg_buf, &_jpg_buf_len)) {
                ESP_LOGE(TAG, "JPEG compression failed");
                res = ESP_FAIL;
            }
        } else {
            res = ESP_FAIL;
        }

        if (res == ESP_OK) {
            res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY));
            if (res == ESP_OK) {
                size_t hlen = snprintf((char *)part_buf, 128, _STREAM_PART, _jpg_buf_len, _timestamp.tv_sec, _timestamp.tv_usec);
                res = httpd_resp_send_chunk(req, (const char *)part_buf, hlen);
            }
            if (res == ESP_OK) {
                res = httpd_resp_send_chunk(req, (const char *)_jpg_buf, _jpg_buf_len);
            }

            if (frame->format != PIXFORMAT_JPEG) {
                free(_jpg_buf);
                _jpg_buf = NULL;
            }
        }

        if (gReturnFB) {
            esp_camera_fb_return(frame);
        } else {
            free(frame->buf);
        }

        if (res != ESP_OK) {
            ESP_LOGE(TAG, "Break stream handler");
            break;
        }
    }

    return res;
}


extern const char root_start[] asm("_binary_root_html_start");
extern const char root_end[] asm("_binary_root_html_end");

static esp_err_t root_get_handler(httpd_req_t *req)
{
    const uint32_t root_len = root_end - root_start;

    ESP_LOGI(TAG, "Serve root");
    httpd_resp_set_type(req, "text/html");
    httpd_resp_send(req, root_start, root_len);

    return ESP_OK;
}


esp_err_t action_handler(httpd_req_t *req) {
    char*  buf;
    size_t buf_len;
    esp_rom_gpio_pad_select_gpio(LED_NUM_33);
    esp_rom_gpio_pad_select_gpio(LED_NUM_12);
    esp_rom_gpio_pad_select_gpio(LED_NUM_13);
    esp_rom_gpio_pad_select_gpio(LED_NUM_4);
    gpio_set_direction(LED_NUM_33,  GPIO_MODE_OUTPUT);
    gpio_set_direction(LED_NUM_12,  GPIO_MODE_OUTPUT);
    gpio_set_direction(LED_NUM_13,  GPIO_MODE_OUTPUT);
    gpio_set_direction(LED_NUM_4,  GPIO_MODE_OUTPUT);
    buf_len = httpd_req_get_url_query_len(req) + 1;
    if (buf_len > 1) {
        buf = malloc(buf_len);
        if(httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) {
            char param[32];
            if (httpd_query_key_value(buf, "go", param, sizeof(param)) == ESP_OK) {
                if (strcmp(param, "forward") == 0) {
                    ESP_LOGI(TAG, "forward");
                    gpio_set_level(LED_NUM_33,1);
                    gpio_set_level(LED_NUM_4,1);

                    gpio_set_level(LED_NUM_12,1);
                    gpio_set_level(LED_NUM_13,0);
                } else if (strcmp(param, "left") == 0) {
                    ESP_LOGI(TAG, "left");
                    gpio_set_level(LED_NUM_33,0);
                    gpio_set_level(LED_NUM_4,0);
                    gpio_set_level(LED_NUM_12,0);
                    gpio_set_level(LED_NUM_13,1);
                } else if (strcmp(param, "right") == 0) {
                    ESP_LOGI(TAG, "right");
                } else if (strcmp(param, "backward") == 0) {
                    ESP_LOGI(TAG, "backward");
                } else if (strcmp(param, "stop") == 0) {
                    ESP_LOGI(TAG, "stop");
                }
            }
        }
        free(buf);
    }
    httpd_resp_send(req, NULL, 0); // 发送空响应
    return ESP_OK;
}


httpd_handle_t start_stream_server(const QueueHandle_t frame_i, const bool return_fb)
{
    xQueueFrameI = frame_i;
    gReturnFB = return_fb;

    httpd_config_t config = HTTPD_DEFAULT_CONFIG();
    config.stack_size = 5120;

    httpd_uri_t action_uri= {
        .uri = "/action",
        .method = HTTP_GET,
        .handler = action_handler
    };

    httpd_uri_t stream_uri = {
        .uri = "/stream",
        .method = HTTP_GET,
        .handler = stream_handler,
        .user_ctx = NULL
        
    };
    httpd_uri_t web_uri = {
        .uri = "/",
        .method = HTTP_GET,
        .handler = root_get_handler
    };
    
  
    httpd_handle_t server = NULL;

    config.max_open_sockets = 7;
    config.lru_purge_enable = true;

   config.server_port = 80;
   if (httpd_start(&camera_httpd, &config) == ESP_OK) {
       httpd_register_uri_handler(camera_httpd, &web_uri);
       httpd_register_uri_handler(camera_httpd, &action_uri);
    }

   //图传和静态页面要分开设置，要不只能看图像。不能操作 
   config.server_port = 81;
   config.ctrl_port = 32769;

  if (httpd_start(&stream_httpd, &config) == ESP_OK) {
    httpd_register_uri_handler(stream_httpd, &stream_uri);
  }

    return server;
}
